/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.common.base.service.rpc.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.je.common.base.DynaBean;
import com.je.common.base.constants.api.InterfaceConstant;
import com.je.common.base.entity.api.ChildInfoEntity;
import com.je.common.base.entity.api.OperationInfoEntity;
import com.je.common.base.entity.api.VerificationParameterResultEntity;
import com.je.common.base.mapper.query.Condition;
import com.je.common.base.mapper.query.ConditionEnum;
import com.je.common.base.mapper.query.NativeQuery;
import com.je.common.base.mapper.query.Query;
import com.je.common.base.service.*;
import com.je.common.base.util.StringUtil;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import org.apache.commons.collections.map.HashedMap;
import org.apache.servicecomb.provider.pojo.RpcSchema;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RpcSchema(schemaId = "commonApiService")
public class CommonApiServiceImpl  implements CommonApiService {

    @Autowired
    private MetaService metaService;

    @Autowired
    private QueryBuilderService queryBuilderService;

    @Autowired
    private MetaResourceService metaResourceService;

    @Autowired
    private CommonService commonService;

    @Override
    public Map<String, Object> executeUpdate(DynaBean apiInfo, JSONArray parameterArr, List<DynaBean> inputParameter) {
        Map<String,Object> result = new HashMap<>();
        int updateCount = 0;
        OperationInfoEntity operationInfoEntity = getOperationBaseInfo(apiInfo);
        for(int i = 0;i<parameterArr.size();i++){
            ConditionsWrapper conditionsWrapper = ConditionsWrapper.builder();
            JSONObject parameter = parameterArr.getJSONObject(i);
            String whereSql = formatVariable(conditionsWrapper,queryBuilderService.trimSql(operationInfoEntity.getWhereSql()),parameter,apiInfo);
            conditionsWrapper.apply(whereSql);
            DynaBean dynaBean =  buildDynaBean(operationInfoEntity,parameter);
            commonService.buildModelModifyInfo(dynaBean);
            int count = metaService.update(dynaBean,conditionsWrapper);
            updateCount+=count;
        }
        result.put("updateCount",updateCount);
        return result;
    }

    @Override
    public Map<String, Object> executeInsert(DynaBean apiInfo, JSONArray parameterArr) {
        Map<String,Object> result = new HashMap<>();
        List<DynaBean> list = new ArrayList<>();
        OperationInfoEntity operationInfoEntity = getOperationBaseInfo(apiInfo);
        for(int i = 0;i<parameterArr.size();i++){
            JSONObject parameter = parameterArr.getJSONObject(i);
            DynaBean dynaBean =  buildDynaBean(operationInfoEntity,parameter);
            commonService.buildModelCreateInfo(dynaBean);
            list.add(dynaBean);
        }
        metaService.insertBatch(list);
        result.put("insertCount",list.size());
        return result;
    }

    private DynaBean buildDynaBean(OperationInfoEntity operationInfoEntity, JSONObject parameter) {
        DynaBean dynaBean = new DynaBean(operationInfoEntity.getTableCode(),false);
        for (Map.Entry<String, Object> entry : parameter.entrySet()) {
            String value = entry.getValue()==null?"":entry.getValue().toString();
            dynaBean.put(entry.getKey(),entry.getValue());
        }
        return dynaBean;
    }

    @Override
    public List<Map<String,Object>> executeQuery(DynaBean apiInfo, JSONObject parameter, List<DynaBean> inputParameter, List<DynaBean> outputParameter) {
        OperationInfoEntity operationInfoEntity =  buildOperationInfo(apiInfo,parameter,inputParameter,outputParameter);
        String tableCode =operationInfoEntity.getTableCode();
        String selectColums =operationInfoEntity.getSelectColums();
        String whereSql =operationInfoEntity.getWhereSql();
        String orderSql =operationInfoEntity.getOrderBySql();
        String limit =operationInfoEntity.getLimit();
        ConditionsWrapper conditionsWrapper = ConditionsWrapper.builder();
        conditionsWrapper.table(tableCode);
        if(StringUtil.isNotEmpty(selectColums)){
            conditionsWrapper.selectColumns(selectColums);
        }
        if(StringUtil.isNotEmpty(whereSql)){
            //格式化参数
            whereSql =formatVariable(conditionsWrapper,queryBuilderService.trimSql(whereSql),parameter,apiInfo);
            conditionsWrapper.apply(whereSql);
        }
        conditionsWrapper.apply(orderSql);
        if(StringUtil.isEmpty(conditionsWrapper.getSql())){
            conditionsWrapper.apply(" 1=1 ");
        }
        conditionsWrapper.apply(StringUtil.isNotEmpty(limit),limit);
        List<Map<String,Object>> list = metaService.selectSql(conditionsWrapper);
        if(StringUtil.isNotEmpty(operationInfoEntity.getChildCodes())){
            list = addChildData(list,operationInfoEntity);
        }
        return list;
    }

    private List<Map<String, Object>> addChildData(List<Map<String,Object>> list,OperationInfoEntity operationInfoEntity) {
        Map<String,ChildInfoEntity> childMap = operationInfoEntity.getChildInfo();
        for(Map<String,Object> data:list){
            String[] childCodesArr = operationInfoEntity.getChildCodes().split(",");
            for(String childCode:childCodesArr){
                data.put(childCode,getChildData(data,childMap.get(childCode)));
            }
        }
        return list;
    }

    private List<Map<String,Object>> getChildData(Map<String, Object> data, ChildInfoEntity childInfoEntity) {
        if(childInfoEntity==null){
            return null;
        }
        String whereSql="";
        ConditionsWrapper conditionsWrapper = ConditionsWrapper.builder();
        List<Condition> conditionList =childInfoEntity.getQuery().getCustom();
        if(conditionList.size()>0){
            conditionsWrapper.and( i -> {
                for (Condition p : conditionList) {
                    queryBuilderService.condition(i, p, data);
                }
            });
        }
        conditionsWrapper.table(childInfoEntity.getTableCode());
        if(StringUtil.isNotEmpty(childInfoEntity.getSelectColumns())){
            conditionsWrapper.selectColumns(childInfoEntity.getSelectColumns());
        }
        conditionsWrapper.apply(StringUtil.isNotEmpty(whereSql),whereSql);
        List<Map<String,Object>> list = metaService.selectSql(conditionsWrapper);
        return list;
    }

    @Override
    public Map<String, Object> executeDelete(DynaBean apiInfo, JSONArray parameterArr, List<DynaBean> inputParameter) {
        Map<String,Object> result = new HashMap<>();
        int delCount = 0;
        for(int i = 0;i<parameterArr.size();i++){
            ConditionsWrapper conditionsWrapper = ConditionsWrapper.builder();
            JSONObject parameter = parameterArr.getJSONObject(i);
            OperationInfoEntity operationInfoEntity =  buildOperationInfo(apiInfo,parameter,inputParameter,null);
            String whereSql = formatVariable(conditionsWrapper,queryBuilderService.trimSql(operationInfoEntity.getWhereSql()),parameter,apiInfo);
            conditionsWrapper.table(operationInfoEntity.getTableCode()).apply(whereSql);
            int count = metaService.delete(conditionsWrapper);
            delCount+=count;
        }
        result.put("deleteCount",delCount);
        return result;
    }

    private OperationInfoEntity buildOperationInfo(DynaBean apiInfo, JSONObject parameter, List<DynaBean> inputParameter, List<DynaBean> outputParameter) {
        OperationInfoEntity operationInfoEntity = getOperationBaseInfo(apiInfo);
        String whereSql = buildWhereSql(operationInfoEntity.getWhereSql(),parameter,inputParameter);
        if(StringUtil.isNotEmpty(whereSql)){
            operationInfoEntity.setWhereSql(whereSql);
        }
        Map<String,Object> map = null;
        if(outputParameter!=null&&outputParameter.size()>0){
            map = getSelectColums(outputParameter);
            operationInfoEntity.setSelectColums(map.get("parentSelectColums")==null?"":map.get("parentSelectColums").toString());
            operationInfoEntity.setChildCodes(map.get("childCodes")==null?"":map.get("childCodes").toString());
            operationInfoEntity.setChildIds(map.get("childIds")==null?"":map.get("childIds").toString());
            operationInfoEntity.setChildInfo((Map<String, ChildInfoEntity>) map.get("childInfo"));
        }
        if(StringUtil.isNotEmpty(operationInfoEntity.getChildIds())){
            switch (apiInfo.getStr("INTERFACE_DATA_SOURCE")){
                case InterfaceConstant.DATA_SOURCE_FUNC:
                    buildFuncAssociation(apiInfo,operationInfoEntity.getChildIds(),operationInfoEntity.getChildCodes(),operationInfoEntity.getChildInfo());
                    break;
                case InterfaceConstant.DATA_SOURCE_TABLE:
                    buildTableAssociation(apiInfo,operationInfoEntity.getChildIds(),operationInfoEntity.getChildCodes(),operationInfoEntity.getChildInfo());
                    break;
            }
        }

        return operationInfoEntity;
    }

    private void buildTableAssociation(DynaBean apiInfo, String childIds, String childCodes, Map<String, ChildInfoEntity> childInfo) {
        String[] childIdsArr = childIds.split(",");
        String[] childCodesArr = childCodes.split(",");
        for(int i=0;i<childIdsArr.length;i++){
            ChildInfoEntity childInfoEntity = childInfo.get(childCodesArr[i]);
            List<DynaBean> keyList = metaResourceService.selectByTableCodeAndNativeQuery("JE_CORE_TABLEKEY",
                    NativeQuery.build().eq("TABLEKEY_RESOURCETABLE_ID",childIdsArr[i]).eq("TABLEKEY_TYPE","Foreign"));
            String tableCode = keyList!=null&&keyList.size()>0?keyList.get(0).getStr("TABLEKEY_TABLECODE"):null;
            childInfoEntity.setTableCode(tableCode);
            List<Condition> custom = new ArrayList<>();
            for(DynaBean key:keyList){
                custom.add(new Condition(key.getStr("TABLEKEY_COLUMNCODE"), ConditionEnum.EQ.getType(),
                        "{"+ key.getStr("TABLEKEY_LINECOLUMNCODE")+"}"));
            }
            Query query = new Query();
            query.setCustom(custom);
            childInfoEntity.setQuery(query);
        }
    }

    private void buildFuncAssociation(DynaBean apiInfo, String childIds,String childCodes,Map<String,ChildInfoEntity> childInfos) {
        String[] childIdsArr = childIds.split(",");
        String[] childCodesArr = childCodes.split(",");
        for(int i =0;i<childIdsArr.length;i++){
            DynaBean dynaBean = metaResourceService.selectOneByNativeQuery("JE_CORE_FUNCRELATION",
                    NativeQuery.build().eq("JE_CORE_FUNCRELATION_ID",childIdsArr[i]));
            List<DynaBean> associationList = metaResourceService.selectByTableCodeAndNativeQuery("JE_CORE_ASSOCIATIONFIELD",
                    NativeQuery.build().eq("ASSOCIATIONFIELD_FUNCRELAT_ID",dynaBean.getStr("JE_CORE_FUNCRELATION_ID")));
            String childCode = childCodesArr[i];
            ChildInfoEntity childInfoEntity = childInfos.get(childCode);
            childInfoEntity.setTableCode(dynaBean.getStr("FUNCRELATION_TABLENAME"));
            List<Condition> custom = new ArrayList<>();
            for(DynaBean association:associationList){
                String associationType = association.getStr("ASSOCIATIONFIELD_ASSOCIATION");
                if("no".equals(associationType)){
                    continue;
                }
                if("idit".equals(associationType) &&
                        StringUtil.isNotEmpty(association.getStr("ASSOCIATIONFIELD_SQL"))){
                    custom.addAll(JSON.parseArray(association.getStr("ASSOCIATIONFIELD_SQL"),Condition.class));
                }else if(StringUtil.isNotEmpty(associationType)){
                    custom.add(new Condition(association.getStr("ASSOCIATIONFIELD_CHIFIELDCODE"),
                            associationType,
                            "{"+association.getStr("ASSOCIATIONFIELD_PRIFIELDCODE")+"}"));
                }
            }
            Query query = new Query();
            query.setCustom(custom);
            childInfoEntity.setQuery(query);
        }
    }

    private String formatVariable(ConditionsWrapper conditionsWrapper,String whereSql,JSONObject parameter,DynaBean apiInfo) {
        Map<String,Object> map = buildFormatVariableMap(parameter,apiInfo);
        return queryBuilderService.formatVariable(conditionsWrapper,whereSql,map);
    }

    private Map<String, Object> buildFormatVariableMap(JSONObject parameter,DynaBean apiInfo) {
        Map<String,Object> map = new HashedMap();
        map.put("JE_JRI_INTERFACE_ID",apiInfo.getStr("JE_JRI_INTERFACE_ID"));
        map.put("INTERFACE_NAME",apiInfo.getStr("INTERFACE_NAME"));
        map.put("USER_CODE",apiInfo.getStr("USER_CODE"));
        map.put("JE_JRI_USER_AUTHORIZE_ID",apiInfo.getStr("JE_JRI_USER_AUTHORIZE_ID"));
        map.put("USER_NAME",apiInfo.getStr("USER_NAME"));
        for (Map.Entry<String, Object> entry : parameter.entrySet()) {
            String value = entry.getValue()==null?"":entry.getValue().toString();
            if(StringUtil.isNotEmpty(value)){
                map.put(entry.getKey(),value);
            }
        }
        return map;
    }

    private String buildWhereSql(String whereSqlOfFunc, JSONObject parameterJson, List<DynaBean> inputParameter) {
        String whereSql ="";
        for(int i= 0;i<inputParameter.size();i++){
            DynaBean parameter = inputParameter.get(i);
            String code =  parameter.getStr("PARAMETER_CODE");
            String value =  parameter.getStr("PARAMETER_DEFAULT");
            if(parameterJson.containsKey(code)){
                value = parameterJson.getString(code)==null?value:parameterJson.getString(code);
            }
            if(StringUtil.isNotEmpty(value)){
                if(StringUtil.isEmpty(whereSql)){
                    whereSql += code+"='"+value+"' ";
                }else{
                    whereSql += " AND "+code+"='"+value+"' ";
                }
            }
        }
        if(StringUtil.isNotEmpty(whereSqlOfFunc)){
            whereSql =whereSql+whereSqlOfFunc;
        }
        return whereSql;
    }

    private Map<String,Object> getSelectColums(List<DynaBean> outputParameterList) {
        Map<String,Object> map = new HashMap<>();
        String parentSelectColums ="";
        String childId ="";
        String childCode ="";
        List<DynaBean> childFieldList = new ArrayList<>();
        Map<String,ChildInfoEntity> childMap = new HashMap<>();
        for(int i = 0;i<outputParameterList.size();i++){
            DynaBean outputParameter = outputParameterList.get(i);
            String code = outputParameter.getStr("PARAMETER_CODE");
            if(InterfaceConstant.COMMON_YES.equals(outputParameter.getStr("PARAMETER_ISCHILD"))){
                String resourcesId = outputParameter.getStr("PARAMETER_RESOURCES_ID");
                childCode+=code+",";
                childId+=resourcesId+",";
            }else if(StringUtil.isEmpty(outputParameter.getStr("PARAMETER_PARENT_CODE"))){
                parentSelectColums+=code+",";
            }
            if(StringUtil.isNotEmpty(outputParameter.getStr("PARAMETER_PARENT_CODE"))){
                childFieldList.add(outputParameter);
            }
        }
        if(StringUtil.isNotEmpty(parentSelectColums)){
            parentSelectColums = parentSelectColums.substring(0, parentSelectColums.length() - 1);
        }
        if(StringUtil.isNotEmpty(childCode)){
            childCode = childCode.substring(0, childCode.length() - 1);
            childId = childId.substring(0, childId.length() - 1);
        }
        if(StringUtil.isNotEmpty(childCode)){
            String[] childCodeArr  = childCode.split(",");
            for(String parentCode:childCodeArr){
                String fields = "";
                for(DynaBean childField:childFieldList){
                    String code = childField.getStr("PARAMETER_CODE");
                    String pCode = childField.getStr("PARAMETER_PARENT_CODE");
                    if(parentCode.equals(pCode)){
                        fields+=code+",";
                    }
                }
                ChildInfoEntity childInfoEntity = new ChildInfoEntity();
                if(StringUtil.isNotEmpty(fields)){
                    fields = fields.substring(0, fields.length() - 1);
                    childInfoEntity.setSelectColumns(fields);
                }
                childMap.put(parentCode,childInfoEntity);
            }
        }

        map.put("parentSelectColums",parentSelectColums);
        map.put("childCodes",childCode);
        map.put("childIds",childId);
        map.put("childInfo",childMap);
        return map;
    }

    private OperationInfoEntity getOperationBaseInfo(DynaBean interfaceInfo) {
        OperationInfoEntity operationInfoEntity = new OperationInfoEntity();
        String tableCode=interfaceInfo.getStr("INTERFACE_TABLECODE");
        String whereSql=interfaceInfo.getStr("INTERFACE_BASE_WHERESQL");
        String orderSql=interfaceInfo.getStr("INTERFACE_ORDER");
        if(InterfaceConstant.DATA_SOURCE_FUNC.equals(interfaceInfo.getStr("INTERFACE_DATA_SOURCE"))){
            DynaBean funcInfo = metaResourceService.selectOneByNativeQuery("JE_CORE_FUNCINFO",
                    NativeQuery.build().eq("FUNCINFO_FUNCCODE",interfaceInfo.getStr("INTERFACE_FUNCINFOCODE")));

            whereSql+=" "+ funcInfo.getStr("FUNCINFO_WHERESQL");
            if(StringUtil.isEmpty(orderSql)){
                orderSql = funcInfo.getStr("FUNCINFO_ORDERSQL");
            }
        }
        operationInfoEntity.setTableCode(tableCode);
        operationInfoEntity.setWhereSql(whereSql);
        operationInfoEntity.setOrderBySql(orderSql);
        if(!"0".equals(interfaceInfo.getStr("INTERFACE_RETURN_DATA_COUNT"))&&
            StringUtil.isNotEmpty(interfaceInfo.getStr("INTERFACE_RETURN_DATA_COUNT"))){
            operationInfoEntity.setLimit(" LIMIT "+interfaceInfo.getStr("INTERFACE_RETURN_DATA_COUNT"));
        }
        return operationInfoEntity;
    }

    @Override
    public VerificationParameterResultEntity checkParameter(DynaBean apiInfo, JSONArray parameterArr) {
        return null;
    }

}
